package com.wingsoft.common.utils.bean;

import com.wingsoft.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * bean转换工具类
 */
@Slf4j
public class BeanConvertUtils {

    /**
     * 对象转xml
     *
     * @param obj 对象
     * @return xml字符串
     */
    public static String parseObjectToXml(Object obj) {
        try {
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");

            StringWriter writer = new StringWriter();
            marshaller.marshal(obj, writer);
            return writer.toString();
        } catch (Exception e) {
            log.error("{}转成xml异常：", obj.getClass().getName(), e);
            throw new ServiceException(obj.getClass().getName() + "转成xml异常");

        }
    }

    /**
     * xml转对象
     *
     * @param xml xml字符串
     * @param t   目标类型
     * @param <T> 目标
     * @return 目标
     */
    public static <T> T parseXmlToObject(String xml, Class<T> t) {
        try {
            JAXBContext context = JAXBContext.newInstance(t);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            // 防止XXE攻击，在创建 Unmarshaller 后添加安全配置
            unmarshaller.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");  // 禁用外部 DTD
            unmarshaller.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // 禁用外部 Schema
            return t.cast(unmarshaller.unmarshal(new StringReader(xml)));
        } catch (Exception e) {
            log.error("xml转对象异常：{}", xml, e);
            throw new ServiceException("xml转对象异常");
        }
    }

    /**
     * 单个类转换
     *
     * @param s           源
     * @param targetClass 目标类型
     * @param <S>         源
     * @param <T>         目标
     * @return 目标
     */
    public static <S, T> T convert(S s, Class<T> targetClass) {
        return convert(s, targetClass, null);
    }

    /**
     * 单个类转换
     *
     * @param s           源
     * @param targetClass 目标类型
     * @param callBack    回调方法
     * @param <S>         源
     * @param <T>         目标
     * @return 目标
     */
    public static <S, T> T convert(S s, Class<T> targetClass, BeanConvertCallBack<S, T> callBack) {
        if (s == null || targetClass == null) {
            return null;
        }
        T t;
        try {
            Constructor<T> constructor = targetClass.getDeclaredConstructor();
            constructor.setAccessible(true); // 处理私有构造器
            t = constructor.newInstance();
        } catch (Exception e) {
            log.error("sourceObj:{},targetClass:{}", s, targetClass, e);
            return null;
        }
        BeanUtils.copyProperties(s, t);

        if (callBack != null) {
            callBack.callBack(s, t);
        }
        return t;
    }

    /**
     * List之间转换
     *
     * @param sourceList  源
     * @param targetClass 目标类型
     * @param <T>         目标
     * @return 目标
     */
    public static <T> List<T> convertList(List<?> sourceList, Class<T> targetClass) {
        return convertList(sourceList, targetClass, null);
    }

    /**
     * List之间转换
     *
     * @param sourceList  源
     * @param targetClass 目标类型
     * @param callBack    回调方法
     * @param <S>         源
     * @param <T>         目标
     * @return 目标
     */
    public static <S, T> List<T> convertList(List<S> sourceList, Class<T> targetClass, BeanConvertCallBack<S, T> callBack) {
        if (CollectionUtils.isEmpty(sourceList) || targetClass == null) {
            return Collections.emptyList();
        }
        return sourceList.stream().map(sourceObj -> convert(sourceObj, targetClass, callBack)).collect(Collectors.toList());
    }

    /**
     * 回调方法
     *
     * @param <S> 源
     * @param <T> 目标
     */
    @FunctionalInterface
    public interface BeanConvertCallBack<S, T> {
        void callBack(S s, T t);
    }
}
